#ifndef IOC_HPP
#define IOC_HPP

#include <type_traits>
#include <string>
#include <memory>
#include <typeinfo>
#include <unordered_map>
#include <functional>
#include <stdexcept>
#include <mutex>

/**
 * 托管的类型信息：
 *      1. 类型ID
 *      2. Map<Key,Getter>
 *          a) Key: 注册时的名称，空名称为默认值
 *          b) Getter: 区别单实例和多实例，行为不同
 */

namespace IoC
{
    using namespace std;

    namespace details {
        /**
         * 获取器抽象类
         */
        class BaseGetter
        {
            bool const m_is_singleton_;
        public:
            BaseGetter(bool singleton): m_is_singleton_(singleton) {}
            virtual ~BaseGetter() = default;

            bool IsSingleton() const
            {
                return m_is_singleton_;
            }

            /**
             * 在多实例模式下这个一定返回的是裸指针
             * 在单实例模式下这个返回的是shared_ptr的指针
             */
            template<typename T>
            T* Get()
            {
                return reinterpret_cast<T*>(Data());
            }

        protected:
            virtual void* Data() = 0;
        };

        /**
         * 有默认构造函数的类，多实例模式
         */
        template <typename T, typename I = T>
        class DefaultClassFactory final : public BaseGetter
        {
        public:
            DefaultClassFactory(): BaseGetter(false) {}
            void* Data() override
            {
                return static_cast<I*>(new T{});
            }
        };

        /**
         * 用户指定构造方法的类，多实例模式
         */
        template <typename T, class F>
        class UserClassFactory final: public BaseGetter
        {
            F m_factory_;
        public:
            UserClassFactory(F f):BaseGetter(false), m_factory_(f)
            {
                // if (m_factory_ == nullptr)
                //     throw invalid_argument("bad factory function!!");
            }

            void* Data() override
            {
                return static_cast<T*>(m_factory_());
            }
        };

        /**
         * 具有默认构造的类，单实例模式
         */
        template<typename T, typename I = T>
        class DefaultSingletonClass final: public BaseGetter
        {
            std::shared_ptr<I> m_instance_;
        public:
            DefaultSingletonClass(): BaseGetter(true) {}

            void* Data() override
            {
                if (!m_instance_)
                    m_instance_ = std::make_shared<T>();
                return &m_instance_;
            }
        };

        /**
         * 用户指定构造方法，单实例模式
         */
        template<typename C, typename F>
        class UserSingletonClass final: public BaseGetter
        {
            std::shared_ptr<C> m_instance_;
            F m_creator_;
        public:
            UserSingletonClass(F creator): BaseGetter(true), m_creator_(creator) {}

            void* Data() override
            {
                if (!m_instance_)
                {
                    m_instance_ = std::shared_ptr<typename remove_pointer<typename std::result_of<F()>::type>::type>{ m_creator_() };
                }
                return &m_instance_;
            }
        };

        /**
         * 单实列模式，用户实例的对象，可选是否由容器托管
         */
        template<typename C>
        class UserAllocSingleClass final : public BaseGetter
        {
            std::shared_ptr<C> m_instance_;
            
            struct CustomDeleter
            {
                bool const m_managed_;
                CustomDeleter(bool release): m_managed_(release) {}

                template<typename T>
                void operator()(T *ptr)
                {
                    if (m_managed_)
                        delete ptr;
                }
            };

        public:
            template <typename T>
            UserAllocSingleClass(T* instance, bool managed = true, typename enable_if<is_convertible<T*, C*>::value>::type* = nullptr) :
                BaseGetter(true), m_instance_(std::shared_ptr<T>{instance, CustomDeleter{managed}})
            {
                if (m_instance_ == nullptr)
                    throw invalid_argument("bad value!!");
            }

            void* Data() override 
            {
                return &m_instance_;
            }
        };
    }

    // 这个可以修改为自定义类，具有相似特性就行
    using tid_t = typename result_of<decltype(&type_info::hash_code)(type_info*)>::type;

    // 类似上边
    template <typename T>
    tid_t GetTypeId()
    {
        static tid_t s_tid{ typeid(T).hash_code() };
        return s_tid;
    }

    template<typename T>
    using Ptr = shared_ptr<T>;

    /**
     * Inverse Of Control Container
     */
    class Container final {
        // data
        using GetterMap = unordered_map<string, details::BaseGetter*>;
        unordered_map<tid_t, GetterMap> m_types_;
        mutable recursive_mutex m_mtx_;
    public:
        Container();
        ~Container();

        // 禁止复制和移动
        Container(const Container&) = delete;
        Container(Container&&) = delete;
        Container& operator=(const Container&) = delete;
        Container& operator=(Container&&) = delete;

        /// Begin Multi-Instance

        /**
         *  注册具有可访问的默认构造的多实例类
         *  C: 类型参数
         */
        template<typename C>
        Container& Register(
            std::string name = "",
            typename enable_if<is_default_constructible<C>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<C>(), name, new details::DefaultClassFactory<C>{});
            return *this;
        }

        /**
         *  注册自定义创建方发的类型，这个重载的模板参数不用指定，均由编译器自动推断。
         */
        template<typename F,
            typename C = typename remove_pointer<typename result_of<F()>::type>::type>
        Container& Register(
            F &&factory,
            std::string name = "",
            typename enable_if<is_convertible<typename result_of<F()>::type, C*>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<C>(), name, new details::UserClassFactory<C,F>(forward<F>(factory)));
            return *this;
        }

        /**
         *  注册自定义创建方发的类型，这个重载可以指定类型，可以为创建函数的返回类型的基类
         */
        template<typename C,
            typename F>
        Container& Register(
            F&& factory,
            std::string name = "",
            typename enable_if<is_convertible<typename result_of<F()>::type, C*>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<C>(), name, new details::UserClassFactory<C, F>(forward<F>(factory)));
            return *this;
        }

        /**
         *  注册具有可访问的默认构造函数的子类为基类的类型
         */
        template<typename Interface,
            typename Implement>
        Container& Register(
            std::string name = "",
            typename enable_if<is_base_of<Interface, Implement>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<Interface>(), name, new details::DefaultClassFactory<Implement, Interface>{});
            return *this;
        }
        // End Multi-Instance

        // Begin Single-Instance
        /**
         *  单例模式重载，类C具有可访问的默认构造函数
         */
        template<typename C>
        Container& RegisterSingleton(
            std::string name = "",
            typename enable_if<is_default_constructible<C>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<C>(), name, new details::DefaultSingletonClass<C>{});
            return *this;
        }

        /**
         *  单例模式重载，提供创建方法，类型为创建方法的返回值类型，无需提供模板参数
         */
        template<typename F,
            typename C = typename remove_pointer<typename result_of<F()>::type>::type>
        Container& RegisterSingleton(
            F&& factory,
            std::string name = "",
            typename enable_if<is_convertible<typename result_of<F()>::type, C*>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<C>(), name, new details::UserSingletonClass<C, F>(forward<F>(factory)));
            return *this;
        }

        /**
         *  单例模式重载，提供创建方法，类型可指定
         */
        template<typename C,
            typename F>
        Container& RegisterSingleton(
            F&& factory,
            std::string name = "",
            typename enable_if<is_convertible<typename result_of<F()>::type, C*>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<C>(), name, new details::UserSingletonClass<C, F>(forward<F>(factory)));
            return *this;
        }

        /**
         *  单例模式重载，具有可访问的默认构造函数的子类为父类的创建方法
         */
        template<typename Interface,
            typename Implement>
        Container& RegisterSingleton(
            std::string name = "",
            typename enable_if<is_base_of<Interface, Implement>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<Interface>(), name, new details::DefaultSingletonClass<Implement, Interface>{});
            return *this;
        }

        /**
         *  单例模式重载，无需提供模板参数，注册类型C的实例为单实例对象
         */
        template<typename C, typename I = C>
        Container& RegisterSingleton(
            C* instance,
            std::string name = "",
            typename enable_if<is_base_of<I, C>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<I>(), name, new details::UserAllocSingleClass<I>(instance));
            return *this;
        }

        /**
         *  单例模式重载
         */
        template<typename I, typename C>
        Container& RegisterSingleton(
            C* instance,
            std::string name = "",
            typename enable_if<is_base_of<I, C>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<I>(), name, new details::UserAllocSingleClass<I>(instance));
            return *this;
        }

        /**
         *  单例模式重载
         */
        template<typename C, typename I = C>
        Container& RegisterSingletonUnManagement(
            C* instance,
            std::string name = "",
            typename enable_if<is_base_of<I, C>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<I>(), name, new details::UserAllocSingleClass<I>(instance, false));
            return *this;
        }

        /**
         *  单例模式重载
         */
        template<typename I, typename C>
        Container& RegisterSingletonUnManagement(
            C* instance,
            std::string name = "",
            typename enable_if<is_base_of<I, C>::value>::type* = nullptr
        )
        {
            Add(GetTypeId<I>(), name, new details::UserAllocSingleClass<I>(instance, false));
            return *this;
        }

        // End Single-Instance
        /**
         *  取消注册类型
         */
        template<typename T>
        void UnRegister(const std::string &name = {})
        {
            Remove(GetTypeId<T>(), name);
        }

        // Begin Resolve
        /**
         *  获取指定类型的实例，非异常安全！！
         */
        template<typename T>
        Ptr<T> Resolve(const std::string& name = {}) const
        {
            lock_guard<decltype(m_mtx_)> lck(m_mtx_);

            const auto tid = GetTypeId<T>();
            if (m_types_.find(tid) == end(m_types_))
                throw out_of_range("type not registered!!"); // TODO: Enhancement #1 自动注册可以默认实列的类
            auto const& con = m_types_.at(tid);
            if (con.find(name) == end(con))
                throw out_of_range("no such key: " + name);
            auto getter = con.at(name);
            if (getter->IsSingleton())
            {
                return *(getter->template Get<Ptr<T>>());
            }
            else
            {
                return Ptr<T>(getter->template Get<T>());
            }
        }
        // End Resolve

        static Container* Current();
    private:
        /**
         * \brief 添加类型信息
         * \param type 类型ID
         * \param key 指定名称
         * \param getter 获取器抽象
         */
        void Add(tid_t type, const std::string& key, details::BaseGetter* getter);

        /**
         * \brief 移除类型信息
         * \param type 类型ID
         * \param key 指定名称
         */
        void Remove(tid_t type, const std::string& key);
    };

    template <typename T>
    class DependencyItem final
    {
        Ptr<T> m_ptr_;
    public:
        DependencyItem(const std::string &key ={}): m_ptr_(Container::Current()->Resolve<T>(key)) {}
        ~DependencyItem() {}

        DependencyItem(const DependencyItem&) = delete;
        DependencyItem(DependencyItem&&) = delete;
        DependencyItem& operator=(const DependencyItem&) = delete;
        DependencyItem& operator=(DependencyItem&&) = delete;

        Ptr<T> operator->() const { return m_ptr_; }
    };

} // namespace IoC


#endif // IOC_HPP
